

/*
Copyright (C) 2004 Geoffrey Alan Washburn

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
USA.
 */
import java.net.Socket;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.JTable;
import javax.swing.JOptionPane;
import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.Map;
import javax.swing.BorderFactory;
import java.util.concurrent.ConcurrentHashMap;

/**
 * The entry point and glue code for the game.  It also contains some helpful
 * global utility methods.
 * @author Geoffrey Washburn &lt;<a href="mailto:geoffw@cis.upenn.edu">geoffw@cis.upenn.edu</a>&gt;
 * @version $Id: Mazewar.java 371 2004-02-10 21:55:32Z geoffw $
 */
public class Mazewar extends JFrame {

    /**
     * The default width of the {@link Maze}.
     */
    private final int mazeWidth = 20;
    /**
     * The default height of the {@link Maze}.
     */
    private final int mazeHeight = 10;
    /**
     * The default random seed for the {@link Maze}.
     * All implementations of the same protocol must use
     * the same seed value, or your mazes will be different.
     */
    private final int mazeSeed = 42;
    /**
     * The {@link GUIClient} for the game.
     */
    private GUIClient guiClient = null;
    /**
     * The panel that displays the {@link Maze}.
     */
    private OverheadMazePanel overheadPanel = null;
    /**
     * The table the displays the scores.
     */
    private JTable scoreTable = null;
    /**
     * Create the textpane statically so that we can
     * write to it globally using
     * the static consolePrint methods
     */
    private static final JTextPane console = new JTextPane();

    /**
     * Write a message to the console followed by a newline.
     * @param msg The {@link String} to print.
     */
    public static synchronized void consolePrintLn(String msg) {
        console.setText(console.getText() + msg + "\n");
    }

    /**
     * Write a message to the console.
     * @param msg The {@link String} to print.
     */
    public static synchronized void consolePrint(String msg) {
        console.setText(console.getText() + msg);
    }

    /**
     * Clear the console.
     */
    public static synchronized void clearConsole() {
        console.setText("");
    }
    /** Fields essential for p2p feature */
    /**
     * The {@link Maze} that the game uses.
     */
    private static Maze maze = null;

    public static synchronized Maze getMaze() {
        return maze;
    }
    boolean isHost = true;
    private static int peerID = -1;
    //private List<PeerInfo> playersPool = new Vector<PeerInfo>();
    private static Map<Integer, PeerInfo> playersPool = new ConcurrentHashMap<Integer, PeerInfo>();

    public static void addToSocketPool(Socket peerSocket) {
        throw new UnsupportedOperationException("Not yet implemented");
    }

    public synchronized void addPeerToPool(PeerInfo peer) {
        playersPool.put(peer.getPeerID(), peer);
    }

    public static int getLocalPeerID() {
        return peerID;
    }
    private static InetSocketAddress listenSocket;

    /**
     * Static method for performing cleanup before exiting the game.
     */
    public static void quit() {
        // Put any network clean-up code you might have here.
        // (inform other implementations on the network that you have
        //  left, etc.)

        PeerInfo pLocal = playersPool.get(peerID);

        for (PeerInfo pi : playersPool.values()) {

            if (pi.getPeerID() == peerID) {
                continue;
            }
            Packet pQuit = new Packet(pi.getIPAddress(), pi.getListeningPort(), pi.getPeerID(), PacketType.REQUEST, PacketAction.QUIT);
        }
        System.exit(0);
    }

    /**
     * The place where all the pieces are put together.
     */
    public Mazewar() {
        super("ECE419 Mazewar");
        consolePrintLn("ECE419 Mazewar started!");

        // Create the maze
        maze = new MazeImpl(new Point(mazeWidth, mazeHeight), mazeSeed);
        assert (maze != null);

        // Have the ScoreTableModel listen to the maze to find
        // out how to adjust scores.
        ScoreTableModel scoreModel = new ScoreTableModel();
        assert (scoreModel != null);
        maze.addMazeListener(scoreModel);

        // Throw up a dialog to get the GUIClient name.
        String name = JOptionPane.showInputDialog("Enter your name");
        if ((name == null) || (name.length() == 0)) {
            Mazewar.quit();
        }

        // configure role host / parasite!
        String response = JOptionPane.showInputDialog("Host the game? (y/n)", "y");

        // you are not the host!
        if ("n".equalsIgnoreCase(response) || "no".equalsIgnoreCase(response)) {
            isHost = false;

            // retrieve the remote host and port
            String rsHost = JOptionPane.showInputDialog("Enter address of game host: ");
            String rsPort = JOptionPane.showInputDialog("Enter port of game host: ");
            int rPort = -1;
            try {
                rPort = Integer.parseInt(rsPort);
            } catch (NumberFormatException nfe) {
                // do nothing
            }

            if ((rsHost == null) || rPort < 0) {
                System.out.println("Invalid remote host parameter: " + rsHost + ":" + rsPort);
                Mazewar.quit();
            }

            // User does not know how to follow direction!
        } else if (!"y".equalsIgnoreCase(response) && !"yes".equalsIgnoreCase(name)) {

            System.out.println("Invalid answer to 'Host the game?' question");
            Mazewar.quit();
        }

        Integer listenPort = new Integer(54327);

        // Figure out your listening port
        String portStr = JOptionPane.showInputDialog("Enter port to listen to:", listenPort.toString());

        try {
            listenPort = Integer.parseInt(portStr);
            listenSocket = new InetSocketAddress(listenPort);

        } catch (NumberFormatException nfe) {
            System.out.println("Invalid port number: " + portStr);
            Mazewar.quit();
        } catch (IllegalArgumentException iae) {
            System.out.println("Invalid port number: " + portStr + " - " + iae.getMessage());
            Mazewar.quit();
        }


        // Create the GUIClient and connect it to the KeyListener queue
        guiClient = new GUIClient(name);
        maze.addClient(guiClient);
        this.addKeyListener(guiClient);

        // assemble your personal info
        PeerInfo userPeerInfo = new PeerInfo(name, guiClient, isHost, true, listenSocket.getAddress().getHostAddress(), listenSocket.getPort());
        peerID = userPeerInfo.getPeerID();
        addPeerToPool(userPeerInfo);

        // TODO: create the listening thread
        try {
            new ListeningThread(listenSocket.getPort());
        } catch (IOException e) {
            System.out.println("Cannot initialize the listening thread.");
            Mazewar.quit();
        }

        // TODO: create the host socket



        // Create the panel that will display the maze.
        overheadPanel = new OverheadMazePanel(maze, guiClient);
        assert (overheadPanel != null);
        maze.addMazeListener(overheadPanel);

        // Don't allow editing the console from the GUI
        console.setEditable(false);
        console.setFocusable(false);
        console.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder()));

        // Allow the console to scroll by putting it in a scrollpane
        JScrollPane consoleScrollPane = new JScrollPane(console);
        assert (consoleScrollPane != null);
        consoleScrollPane.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Console"));

        // Create the score table
        scoreTable = new JTable(scoreModel);
        assert (scoreTable != null);
        scoreTable.setFocusable(false);
        scoreTable.setRowSelectionAllowed(false);

        // Allow the score table to scroll too.
        JScrollPane scoreScrollPane = new JScrollPane(scoreTable);
        assert (scoreScrollPane != null);
        scoreScrollPane.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Scores"));

        // Create the layout manager
        GridBagLayout layout = new GridBagLayout();
        GridBagConstraints c = new GridBagConstraints();
        getContentPane().setLayout(layout);

        // Define the constraints on the components.
        c.fill = GridBagConstraints.BOTH;
        c.weightx = 1.0;
        c.weighty = 3.0;
        c.gridwidth = GridBagConstraints.REMAINDER;
        layout.setConstraints(overheadPanel, c);
        c.gridwidth = GridBagConstraints.RELATIVE;
        c.weightx = 2.0;
        c.weighty = 1.0;
        layout.setConstraints(consoleScrollPane, c);
        c.gridwidth = GridBagConstraints.REMAINDER;
        c.weightx = 1.0;
        layout.setConstraints(scoreScrollPane, c);

        // Add the components
        getContentPane().add(overheadPanel);
        getContentPane().add(consoleScrollPane);
        getContentPane().add(scoreScrollPane);

        // Pack everything neatly.
        pack();

        // Let the magic begin.
        setVisible(true);
        overheadPanel.repaint();
        this.requestFocusInWindow();
    }

    /**
     * Entry point for the game.
     * @param args Command-line arguments.
     */
    public static void main(String args[]) {

        /* Create the GUI */
        new Mazewar();
    }
}
